"""
剧场聊天API - 支持角色对话和AI回复
与剧场数据关联的智能对话系统
集成MySQL数据库保存功能
修复了FastAPI lifespan事件处理器的弃用警告
增强的对话数据保存和管理功能
优化版：确保每次对话后数据实时同步到对应剧场的数据库中
Vue前端完全集成版本
"""

import os
import json
import uuid
import requests
import asyncio
import traceback
from typing import Dict, List, Any, Optional
from dataclasses import dataclass, asdict
from datetime import datetime, timedelta
from concurrent.futures import ThreadPoolExecutor
from contextlib import asynccontextmanager

from fastapi import FastAPI, HTTPException, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, FileResponse
from pydantic import BaseModel, Field
import uvicorn

# 尝试导入数据库模块
try:
    from database import get_mysql_manager
    DATABASE_AVAILABLE = True
except ImportError:
    print("⚠️ 数据库模块未找到，将使用文件存储模式")
    DATABASE_AVAILABLE = False
    get_mysql_manager = None

# ==================== 数据模型 ====================

@dataclass
class ChatMessage:
    id: str
    character_id: str
    character_name: str
    message: str
    timestamp: str
    message_type: str  # 'dialogue', 'system', 'action'
    ai_model: Optional[str] = None  # 记录AI模型信息
    response_time_ms: Optional[int] = None  # 记录响应时间

@dataclass
class Character:
    id: str
    name: str
    description: str
    avatar: str
    selectedSkills: List[str]

@dataclass
class ChatSession:
    session_id: str
    theater_id: str
    theater_title: str
    node_id: str
    node_title: str
    node_content: str
    player_character_id: str
    all_characters: List[Character]
    character_settings: Dict[str, Any]
    messages: List[ChatMessage]
    created_at: datetime
    last_active: datetime
    total_messages: int = 0
    ai_responses_count: int = 0

@dataclass
class ConversationBackup:
    """对话备份数据结构"""
    backup_id: str
    session_id: str
    theater_title: str
    node_title: str
    backup_time: datetime
    messages_count: int
    file_path: str

# ==================== API请求模型 ====================

class StartChatRequest(BaseModel):
    """开始聊天请求"""
    theater_title: str
    node_id: str
    node_title: str
    node_content: str
    player_character_id: str
    all_characters: List[Dict[str, Any]]
    character_settings: Dict[str, Any] = {}

class SendMessageRequest(BaseModel):
    """发送消息请求"""
    session_id: str
    message: str

class ApiResponse(BaseModel):
    success: bool
    message: str
    data: Optional[Dict[str, Any]] = None
    error_code: Optional[str] = None

# ==================== 全局存储 ====================

# 活跃的聊天会话
active_sessions: Dict[str, ChatSession] = {}

# 持久化存储（文件存储作为备份，主要使用数据库）
STORAGE_DIR = "chat_storage"
BACKUP_DIR = "conversation_backups"
EXPORT_DIR = "conversation_exports"

# 创建必要的目录
for directory in [STORAGE_DIR, BACKUP_DIR, EXPORT_DIR]:
    os.makedirs(directory, exist_ok=True)

# 全局变量用于存储chat_engine和mysql_manager
chat_engine = None
mysql_manager = None

# ==================== AI聊天引擎 ====================

class TheaterChatEngine:
    def __init__(self):
        self.api_key = os.getenv('GEEKAI_API_KEY')
        if not self.api_key:
            raise ValueError("请设置GEEKAI_API_KEY环境变量")
        
        self.base_url = 'https://geekai.co/api/v1/chat/completions'
        self.headers = {
            'Authorization': f'Bearer {self.api_key}',
            'Content-Type': 'application/json'
        }
        
        # 为每个会话维护对话上下文
        self.conversation_contexts: Dict[str, List[Dict]] = {}

    def initialize_session_context(self, session: ChatSession):
        """初始化会话的AI上下文"""
        
        # 构建角色描述
        characters_desc = []
        for char in session.all_characters:
            skills_text = ', '.join(char.selectedSkills) if char.selectedSkills else '无特殊技能'
            characters_desc.append(f"- {char.name}: {char.description} (技能: {skills_text})")
        
        # 获取玩家角色信息
        player_char = next((c for c in session.all_characters if c.id == session.player_character_id), None)
        player_name = player_char.name if player_char else "玩家"
        
        # 获取角色设置
        char_settings_desc = []
        for char_id, settings in session.character_settings.items():
            char = next((c for c in session.all_characters if c.id == char_id), None)
            if char and char.id != session.player_character_id:
                behaviors = settings.get('behaviors', [])
                if behaviors:
                    char_settings_desc.append(f"  {char.name}: {', '.join(behaviors[:3])}")
        
        system_prompt = f"""
你是一位专业的剧场体验导演，负责控制剧场中除了玩家扮演角色外的所有角色。

📋 剧场信息：
- 剧场名称：{session.theater_title}
- 当前场景：{session.node_title}

🎭 场景内容：
{session.node_content}

👥 所有角色：
{chr(10).join(characters_desc)}

🎮 玩家扮演：{player_name}

⚙️ 角色设置：
{chr(10).join(char_settings_desc) if char_settings_desc else '  无特殊设置'}

📝 你的职责：
1. 当玩家（{player_name}）发言时，控制其他适合的角色进行回复
2. 根据场景内容和角色设置，让角色表现出符合其性格的行为
3. 推进剧情发展，创造有趣的互动
4. 保持角色的一致性和连贯性
5. 回复要自然、生动，符合剧场氛围

🔧 回复格式要求：
请以JSON格式返回角色的回复，格式如下：
{{
    "responses": [
        {{
            "character_id": "角色ID",
            "character_name": "角色名称", 
            "message": "角色说的话",
            "action": "角色的动作描述（可选）"
        }}
    ]
}}

注意：
- 不是每次都要所有角色回复，选择最合适的1-2个角色回复即可
- 回复要符合角色性格和当前场景
- 可以推进剧情或提出新的话题
- 避免重复或无意义的对话
- 当收到特殊消息"[CONTINUE_DIALOGUE]"时，请让其他角色继续推进剧情对话
        """
        
        self.conversation_contexts[session.session_id] = [
            {"role": "system", "content": system_prompt}
        ]

    async def generate_character_responses(self, session: ChatSession, player_message: str) -> List[ChatMessage]:
        """生成其他角色的回复"""
        
        session_id = session.session_id
        start_time = datetime.now()
        
        if session_id not in self.conversation_contexts:
            self.initialize_session_context(session)
        
        # 添加玩家消息到上下文
        player_char = next((c for c in session.all_characters if c.id == session.player_character_id), None)
        player_name = player_char.name if player_char else "玩家"
        
        # 处理特殊的继续对话消息
        if player_message == "[CONTINUE_DIALOGUE]":
            user_prompt = "请让其他角色继续推进剧情，进行自然的对话互动"
        else:
            user_prompt = f"{player_name}: {player_message}"
        
        self.conversation_contexts[session_id].append({
            "role": "user",
            "content": user_prompt
        })
        
        try:
            # 调用AI生成回复
            ai_response = await self.call_ai_api(session_id)
            
            # 计算响应时间
            response_time = int((datetime.now() - start_time).total_seconds() * 1000)
            
            # 解析AI回复
            responses = self.parse_ai_response(ai_response, session, response_time)
            
            # 将AI回复添加到上下文
            self.conversation_contexts[session_id].append({
                "role": "assistant", 
                "content": ai_response
            })
            
            return responses
            
        except Exception as e:
            print(f"❌ 生成角色回复失败: {e}")
            # 返回一个简单的系统回复作为兜底
            return [ChatMessage(
                id=str(uuid.uuid4()),
                character_id="system",
                character_name="系统",
                message="其他角色正在思考中..." if player_message != "[CONTINUE_DIALOGUE]" else "场景中暂时安静下来...",
                timestamp=datetime.now().isoformat(),
                message_type="system",
                ai_model="fallback",
                response_time_ms=int((datetime.now() - start_time).total_seconds() * 1000)
            )]

    async def call_ai_api(self, session_id: str) -> str:
        """调用AI API"""
        payload = {
            "model": "gpt-4o-mini",
            "messages": self.conversation_contexts[session_id],
            "temperature": 0.8,
            "max_tokens": 1000
        }
        
        loop = asyncio.get_event_loop()
        
        def sync_call():
            try:
                response = requests.post(
                    self.base_url,
                    headers=self.headers,
                    json=payload,
                    timeout=30
                )
                response.raise_for_status()
                result = response.json()
                return result['choices'][0]['message']['content']
            except Exception as e:
                raise Exception(f"AI API调用失败: {e}")
        
        with ThreadPoolExecutor() as executor:
            return await loop.run_in_executor(executor, sync_call)

    def parse_ai_response(self, ai_response: str, session: ChatSession, response_time_ms: int) -> List[ChatMessage]:
        """解析AI回复生成ChatMessage列表"""
        messages = []
        
        try:
            # 尝试解析JSON格式
            import re
            json_match = re.search(r'\{.*\}', ai_response, re.DOTALL)
            if json_match:
                data = json.loads(json_match.group())
                responses = data.get('responses', [])
                
                for resp in responses:
                    char_id = resp.get('character_id', '')
                    char_name = resp.get('character_name', '未知角色')
                    message_text = resp.get('message', '')
                    action = resp.get('action', '')
                    
                    # 验证角色ID是否有效且不是玩家角色
                    if char_id and char_id != session.player_character_id:
                        full_message = message_text
                        if action:
                            full_message += f" *{action}*"
                        
                        messages.append(ChatMessage(
                            id=str(uuid.uuid4()),
                            character_id=char_id,
                            character_name=char_name,
                            message=full_message,
                            timestamp=datetime.now().isoformat(),
                            message_type="dialogue",
                            ai_model="gpt-4o-mini",
                            response_time_ms=response_time_ms
                        ))
                    elif char_name and char_name != session.player_character_id:
                        # 如果没有character_id但有character_name，尝试匹配
                        matching_char = next((c for c in session.all_characters if c.name == char_name), None)
                        if matching_char:
                            full_message = message_text
                            if action:
                                full_message += f" *{action}*"
                            
                            messages.append(ChatMessage(
                                id=str(uuid.uuid4()),
                                character_id=matching_char.id,
                                character_name=char_name,
                                message=full_message,
                                timestamp=datetime.now().isoformat(),
                                message_type="dialogue",
                                ai_model="gpt-4o-mini",
                                response_time_ms=response_time_ms
                            ))
            
            # 如果没有解析到有效回复，创建一个随机角色的回复
            if not messages:
                # 选择一个非玩家角色进行回复
                non_player_chars = [c for c in session.all_characters if c.id != session.player_character_id]
                if non_player_chars:
                    import random
                    selected_char = random.choice(non_player_chars)
                    
                    # 提取AI原始回复中的有用内容
                    clean_response = ai_response.strip()
                    if len(clean_response) > 200:
                        clean_response = clean_response[:200] + "..."
                    
                    if not clean_response or clean_response.startswith('{'):
                        clean_response = "（沉思片刻）这个情况确实需要仔细考虑..."
                    
                    messages.append(ChatMessage(
                        id=str(uuid.uuid4()),
                        character_id=selected_char.id,
                        character_name=selected_char.name,
                        message=clean_response,
                        timestamp=datetime.now().isoformat(),
                        message_type="dialogue",
                        ai_model="gpt-4o-mini",
                        response_time_ms=response_time_ms
                    ))
                else:
                    # 如果没有其他角色，创建系统消息
                    messages.append(ChatMessage(
                        id=str(uuid.uuid4()),
                        character_id="system",
                        character_name="系统",
                        message="场景氛围很安静，大家都在静静地观察着情况的发展...",
                        timestamp=datetime.now().isoformat(),
                        message_type="system",
                        ai_model="gpt-4o-mini",
                        response_time_ms=response_time_ms
                    ))
                
        except Exception as e:
            print(f"⚠️ 解析AI回复失败: {e}, 原始回复: {ai_response}")
            
            # 创建一个错误兜底回复
            non_player_chars = [c for c in session.all_characters if c.id != session.player_character_id]
            if non_player_chars:
                import random
                selected_char = random.choice(non_player_chars)
                messages.append(ChatMessage(
                    id=str(uuid.uuid4()),
                    character_id=selected_char.id,
                    character_name=selected_char.name,
                    message="（略有所思）让我想想该怎么回应...",
                    timestamp=datetime.now().isoformat(),
                    message_type="dialogue",
                    ai_model="gpt-4o-mini",
                    response_time_ms=response_time_ms
                ))
            else:
                messages.append(ChatMessage(
                    id=str(uuid.uuid4()),
                    character_id="system",
                    character_name="系统",
                    message="其他角色似乎在深思，暂时没有回应...",
                    timestamp=datetime.now().isoformat(),
                    message_type="system",
                    ai_model="gpt-4o-mini",
                    response_time_ms=response_time_ms
                ))
        
        return messages

# ==================== 集成MySQL的数据管理器 ====================

class ChatDataManager:
    # 类级别的任务集合，防止后台任务被垃圾回收
    _background_tasks = set()

    @staticmethod
    async def save_session_async(session: ChatSession):
        """异步保存会话数据到文件和数据库 - 优化版"""
        try:
            # 保存到文件（原有逻辑保持不变）
            ChatDataManager._save_session_to_file(session)
            
            # 异步保存到MySQL数据库 - 使用改进的方法
            if DATABASE_AVAILABLE and mysql_manager:
                await ChatDataManager._save_session_to_mysql_enhanced(session)
            
        except Exception as e:
            print(f"❌ 保存会话数据失败: {e}")
            print(f"📋 详细错误: {traceback.format_exc()}")

    @staticmethod
    async def _save_session_to_mysql_enhanced(session: ChatSession):
        """增强版的MySQL保存方法"""
        try:
            global mysql_manager
            if not mysql_manager:
                print("⚠️ MySQL管理器未初始化，跳过数据库保存")
                return
            
            # 🔍 验证theater_id是否存在于theaters表中
            theater_id = await ChatDataManager._validate_and_get_theater_id(session.theater_id)
            if not theater_id:
                print(f"❌ 剧场ID {session.theater_id} 在数据库中不存在，跳过保存")
                return
            
            # 获取玩家角色名称
            player_char = next((c for c in session.all_characters if c.id == session.player_character_id), None)
            player_name = player_char.name if player_char else "玩家"
            
            # 🎯 准备优化的会话数据
            session_data = {
                "session_id": session.session_id,
                "theater_id": theater_id,  # 使用验证后的theater_id
                "node_id": session.node_id,
                "node_title": session.node_title,
                "node_content": session.node_content,
                "player_character_id": session.player_character_id,
                "player_character_name": player_name,
                "character_settings": session.character_settings,
                "total_messages": len(session.messages),
                "ai_responses_count": len([msg for msg in session.messages 
                                         if msg.message_type == "dialogue" and msg.character_id != session.player_character_id]),
                "messages": [ChatDataManager._prepare_message_for_db(msg) for msg in session.messages],
                "metadata": {
                    "theater_title": session.theater_title,
                    "all_characters": [asdict(char) for char in session.all_characters],
                    "created_at": session.created_at.isoformat(),
                    "last_active": session.last_active.isoformat(),
                    "sync_timestamp": datetime.now().isoformat(),
                    "api_version": "1.3.0"
                }
            }
            
            # 🚀 异步保存到数据库
            result = await mysql_manager.save_chat_session_async(session_data)
            
            if result.get('success'):
                print(f"✅ 聊天会话已同步到MySQL: {session.session_id} -> 剧场ID: {theater_id} (消息数: {len(session.messages)})")
                
                # 📊 更新统计信息
                await ChatDataManager._update_session_statistics(theater_id, session.node_id, len(session.messages))
            else:
                print(f"❌ MySQL保存失败: {result.get('error', '未知错误')}")
                
        except Exception as e:
            print(f"❌ MySQL同步失败: {e}")
            print(f"📋 详细错误信息: {traceback.format_exc()}")

    @staticmethod
    async def _validate_and_get_theater_id(theater_id_str: str) -> Optional[int]:
        """验证并获取有效的theater_id"""
        try:
            global mysql_manager
            if not mysql_manager:
                return None
            
            # 尝试转换为整数
            theater_id = int(theater_id_str)
            
            # 检查是否存在于theaters表中
            loop = asyncio.get_event_loop()
            
            def check_theater_exists():
                try:
                    with mysql_manager.get_connection() as connection:
                        cursor = connection.cursor()
                        cursor.execute("SELECT id FROM theaters WHERE id = %s", (theater_id,))
                        result = cursor.fetchone()
                        cursor.close()
                        return result
                except Exception as e:
                    print(f"❌ 数据库查询失败: {e}")
                    return None
            
            with ThreadPoolExecutor() as executor:
                result = await loop.run_in_executor(executor, check_theater_exists)
                
                if result:
                    return theater_id
                else:
                    print(f"⚠️ 剧场ID {theater_id} 在数据库中不存在")
                    return None
                    
        except ValueError:
            print(f"❌ 无效的theater_id格式: {theater_id_str}")
            return None
        except Exception as e:
            print(f"❌ 验证theater_id失败: {e}")
            return None

    @staticmethod
    def _prepare_message_for_db(msg: ChatMessage) -> Dict:
        """为数据库存储准备消息数据"""
        return {
            "id": msg.id,
            "message_id": msg.id,  # 为了兼容数据库字段
            "character_id": msg.character_id,
            "character_name": msg.character_name,
            "message": msg.message,
            "timestamp": msg.timestamp,
            "message_type": msg.message_type,
            "ai_model": msg.ai_model,
            "response_time_ms": msg.response_time_ms
        }

    @staticmethod
    async def _update_session_statistics(theater_id: int, node_id: str, message_count: int):
        """更新会话统计信息"""
        try:
            global mysql_manager
            if not mysql_manager:
                return
            
            # 使用线程池执行同步的统计更新
            loop = asyncio.get_event_loop()
            
            def update_stats():
                try:
                    with mysql_manager.get_connection() as connection:
                        cursor = connection.cursor()
                        mysql_manager._update_chat_statistics(cursor, theater_id, node_id)
                        connection.commit()
                        cursor.close()
                        print(f"📊 统计信息已更新: theater_id={theater_id}, node_id={node_id}")
                except Exception as e:
                    print(f"❌ 更新统计信息失败: {e}")
            
            with ThreadPoolExecutor() as executor:
                await loop.run_in_executor(executor, update_stats)
                
        except Exception as e:
            print(f"❌ 异步更新统计失败: {e}")

    @staticmethod
    def save_session(session: ChatSession):
        """同步保存会话数据（兼容原有调用）"""
        try:
            # 保存到文件
            ChatDataManager._save_session_to_file(session)
            
            # 创建异步任务保存到数据库，但要确保任务被正确执行
            try:
                loop = asyncio.get_running_loop()
                # 如果在异步环境中，创建任务
                task = loop.create_task(ChatDataManager._save_session_to_mysql_enhanced(session))
                # 将任务添加到全局任务集合中，防止被垃圾回收
                ChatDataManager._background_tasks.add(task)
                task.add_done_callback(ChatDataManager._background_tasks.discard)
            except RuntimeError:
                # 如果不在异步环境中，使用线程池执行
                import threading
                thread = threading.Thread(
                    target=ChatDataManager._sync_save_to_mysql, 
                    args=(session,)
                )
                thread.daemon = True
                thread.start()
            
        except Exception as e:
            print(f"❌ 保存会话数据失败: {e}")

    @staticmethod
    def _sync_save_to_mysql(session: ChatSession):
        """在新的事件循环中同步保存到MySQL"""
        try:
            # 创建新的事件循环
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                loop.run_until_complete(ChatDataManager._save_session_to_mysql_enhanced(session))
            finally:
                loop.close()
        except Exception as e:
            print(f"❌ 同步MySQL保存失败: {e}")

    @staticmethod
    def _save_session_to_file(session: ChatSession):
        """保存到文件（原有逻辑）"""
        try:
            theater_dir = os.path.join(STORAGE_DIR, f"theater_{session.theater_id}")
            os.makedirs(theater_dir, exist_ok=True)
            
            session_file = os.path.join(theater_dir, f"node_{session.node_id}_session.json")
            
            # 更新统计信息
            session.total_messages = len(session.messages)
            session.ai_responses_count = len([msg for msg in session.messages if msg.message_type == "dialogue" and msg.character_id != session.player_character_id])
            
            # 转换为可序列化的格式
            session_data = {
                "session_id": session.session_id,
                "theater_id": session.theater_id,
                "theater_title": session.theater_title,
                "node_id": session.node_id,
                "node_title": session.node_title,
                "node_content": session.node_content,
                "player_character_id": session.player_character_id,
                "all_characters": [asdict(char) for char in session.all_characters],
                "character_settings": session.character_settings,
                "messages": [asdict(msg) for msg in session.messages],
                "created_at": session.created_at.isoformat(),
                "last_active": session.last_active.isoformat(),
                "total_messages": session.total_messages,
                "ai_responses_count": session.ai_responses_count,
                "metadata": {
                    "version": "1.3.0",
                    "saved_at": datetime.now().isoformat(),
                    "file_size_bytes": 0
                }
            }
            
            with open(session_file, 'w', encoding='utf-8') as f:
                json.dump(session_data, f, ensure_ascii=False, indent=2)
            
            # 计算文件大小
            file_size = os.path.getsize(session_file)
            session_data["metadata"]["file_size_bytes"] = file_size
            
            # 重新保存包含文件大小的数据
            with open(session_file, 'w', encoding='utf-8') as f:
                json.dump(session_data, f, ensure_ascii=False, indent=2)
            
            print(f"✅ 会话数据已保存到文件: {session_file} ({file_size} bytes)")
            
            # 自动创建备份（如果消息数量达到阈值）
            if session.total_messages > 0 and session.total_messages % 20 == 0:
                ChatDataManager.create_conversation_backup(session)
            
        except Exception as e:
            print(f"❌ 保存会话数据到文件失败: {e}")

    @staticmethod
    def load_session(theater_id: str, node_id: str) -> Optional[ChatSession]:
        """从数据库或文件加载会话数据（优先数据库）"""
        try:
            # 首先尝试从MySQL加载
            if DATABASE_AVAILABLE and mysql_manager:
                session = ChatDataManager.load_session_from_mysql(int(theater_id), node_id)
                if session:
                    return session
            
            # 如果MySQL没有，则从文件加载
            return ChatDataManager._load_session_from_file(theater_id, node_id)
            
        except Exception as e:
            print(f"❌ 加载会话数据失败: {e}")
            return None

    @staticmethod
    def load_session_from_mysql(theater_id: int, node_id: str) -> Optional[ChatSession]:
        """从MySQL数据库加载会话数据"""
        try:
            global mysql_manager
            if not mysql_manager:
                return None
            
            # 获取会话数据
            session_data = mysql_manager.get_chat_session_by_theater_node(theater_id, node_id)
            
            if not session_data:
                return None
            
            # 获取会话消息
            messages_result = mysql_manager.get_chat_messages_by_session(session_data['session_id'])
            
            if not messages_result.get('success'):
                return None
            
            messages_data = messages_result['messages']
            
            # 重构ChatMessage对象
            messages = []
            for msg_data in messages_data:
                timestamp = msg_data['metadata'].get('timestamp') if msg_data.get('metadata') else msg_data['created_at'].isoformat()
                
                message = ChatMessage(
                    id=msg_data['message_id'],
                    character_id=msg_data['character_id'],
                    character_name=msg_data['character_name'],
                    message=msg_data['message'],
                    timestamp=timestamp,
                    message_type=msg_data['message_type'],
                    ai_model=msg_data.get('ai_model'),
                    response_time_ms=msg_data.get('response_time_ms')
                )
                messages.append(message)
            
            # 重构角色对象（从metadata中获取）
            metadata = json.loads(session_data.get('session_data', '{}'))
            all_characters_data = metadata.get('all_characters', [])
            characters = [Character(**char_data) for char_data in all_characters_data]
            
            # 创建ChatSession对象
            session = ChatSession(
                session_id=session_data['session_id'],
                theater_id=str(session_data['theater_id']),
                theater_title=metadata.get('theater_title', session_data.get('theater_title', '')),
                node_id=session_data['node_id'],
                node_title=session_data['node_title'],
                node_content=session_data['node_content'],
                player_character_id=session_data['player_character_id'],
                all_characters=characters,
                character_settings=json.loads(session_data.get('character_settings', '{}')),
                messages=messages,
                created_at=session_data['created_at'],
                last_active=session_data['last_active'],
                total_messages=session_data['total_messages'],
                ai_responses_count=session_data['ai_responses_count']
            )
            
            print(f"✅ 从MySQL加载会话成功: {session_data['session_id']}")
            return session
            
        except Exception as e:
            print(f"❌ 从MySQL加载会话失败: {e}")
            return None

    @staticmethod
    def _load_session_from_file(theater_id: str, node_id: str) -> Optional[ChatSession]:
        """从文件加载会话数据（原有逻辑）"""
        try:
            session_file = os.path.join(STORAGE_DIR, f"theater_{theater_id}", f"node_{node_id}_session.json")
            
            if not os.path.exists(session_file):
                return None
            
            with open(session_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            # 重构角色对象
            characters = [Character(**char_data) for char_data in data['all_characters']]
            
            # 重构消息对象
            messages = [ChatMessage(**msg_data) for msg_data in data['messages']]
            
            session = ChatSession(
                session_id=data['session_id'],
                theater_id=data['theater_id'],
                theater_title=data['theater_title'],
                node_id=data['node_id'],
                node_title=data['node_title'],
                node_content=data['node_content'],
                player_character_id=data['player_character_id'],
                all_characters=characters,
                character_settings=data['character_settings'],
                messages=messages,
                created_at=datetime.fromisoformat(data['created_at']),
                last_active=datetime.fromisoformat(data['last_active']),
                total_messages=data.get('total_messages', len(messages)),
                ai_responses_count=data.get('ai_responses_count', 0)
            )
            
            print(f"✅ 从文件加载会话数据: {session_file}")
            return session
            
        except Exception as e:
            print(f"❌ 从文件加载会话数据失败: {e}")
            return None

    @staticmethod
    def get_theater_sessions(theater_id: str) -> List[Dict]:
        """获取剧场的所有会话信息（优先从数据库）"""
        try:
            global mysql_manager
            if DATABASE_AVAILABLE and mysql_manager:
                # 从数据库获取
                result = mysql_manager.get_chat_sessions_by_theater(int(theater_id))
                if result.get('success'):
                    return result.get('sessions', [])
            
            # 从文件获取（兜底方案）
            return ChatDataManager._get_theater_sessions_from_file(theater_id)
            
        except Exception as e:
            print(f"❌ 获取剧场会话失败: {e}")
            return []

    @staticmethod
    def _get_theater_sessions_from_file(theater_id: str) -> List[Dict]:
        """从文件获取剧场会话信息"""
        try:
            theater_dir = os.path.join(STORAGE_DIR, f"theater_{theater_id}")
            if not os.path.exists(theater_dir):
                return []
            
            sessions = []
            for filename in os.listdir(theater_dir):
                if filename.endswith('_session.json'):
                    try:
                        with open(os.path.join(theater_dir, filename), 'r', encoding='utf-8') as f:
                            data = json.load(f)
                        
                        sessions.append({
                            "session_id": data['session_id'],
                            "node_id": data['node_id'],
                            "node_title": data['node_title'],
                            "message_count": len(data['messages']),
                            "ai_responses_count": data.get('ai_responses_count', 0),
                            "last_active": data['last_active'],
                            "created_at": data['created_at'],
                            "file_size_bytes": data.get('metadata', {}).get('file_size_bytes', 0)
                        })
                    except Exception as e:
                        print(f"⚠️ 读取会话文件失败 {filename}: {e}")
                        continue
            
            # 按最后活跃时间排序
            sessions.sort(key=lambda x: x['last_active'], reverse=True)
            return sessions
            
        except Exception as e:
            print(f"❌ 获取剧场会话失败: {e}")
            return []

    @staticmethod
    def create_conversation_backup(session: ChatSession) -> ConversationBackup:
        """创建对话备份"""
        try:
            backup_id = str(uuid.uuid4())
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            
            # 清理文件名中的特殊字符
            safe_theater_title = "".join(c for c in session.theater_title if c.isalnum() or c in (' ', '-', '_')).rstrip()[:30]
            safe_node_title = "".join(c for c in session.node_title if c.isalnum() or c in (' ', '-', '_')).rstrip()[:20]
            
            filename = f"backup_{timestamp}_{safe_theater_title}_{safe_node_title}.json"
            backup_file = os.path.join(BACKUP_DIR, filename)
            
            # 创建备份数据
            backup_data = {
                "backup_id": backup_id,
                "session_id": session.session_id,
                "theater_id": session.theater_id,
                "theater_title": session.theater_title,
                "node_title": session.node_title,
                "backup_time": datetime.now().isoformat(),
                "original_created_at": session.created_at.isoformat(),
                "messages_count": len(session.messages),
                "ai_responses_count": session.ai_responses_count,
                "messages": [asdict(msg) for msg in session.messages],
                "characters": [asdict(char) for char in session.all_characters],
                "character_settings": session.character_settings,
                "statistics": {
                    "dialogue_messages": len([m for m in session.messages if m.message_type == "dialogue"]),
                    "system_messages": len([m for m in session.messages if m.message_type == "system"]),
                    "average_response_time_ms": ChatDataManager._calculate_average_response_time(session.messages)
                }
            }
            
            with open(backup_file, 'w', encoding='utf-8') as f:
                json.dump(backup_data, f, ensure_ascii=False, indent=2)
            
            backup = ConversationBackup(
                backup_id=backup_id,
                session_id=session.session_id,
                theater_title=session.theater_title,
                node_title=session.node_title,
                backup_time=datetime.now(),
                messages_count=len(session.messages),
                file_path=backup_file
            )
            
            print(f"📦 对话备份已创建: {backup_file}")
            return backup
            
        except Exception as e:
            print(f"❌ 创建对话备份失败: {e}")
            raise

    @staticmethod
    def export_conversation_for_analysis(session: ChatSession, export_format: str = "json") -> str:
        """导出对话数据供分析使用"""
        try:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            safe_title = "".join(c for c in session.theater_title if c.isalnum() or c in (' ', '-', '_')).rstrip()[:30]
            
            if export_format.lower() == "json":
                filename = f"export_{timestamp}_{safe_title}.json"
                export_file = os.path.join(EXPORT_DIR, filename)
                
                export_data = {
                    "export_info": {
                        "export_time": datetime.now().isoformat(),
                        "export_format": "json",
                        "session_id": session.session_id
                    },
                    "theater_info": {
                        "title": session.theater_title,
                        "node_title": session.node_title,
                        "node_content": session.node_content
                    },
                    "conversation_data": {
                        "total_messages": len(session.messages),
                        "conversation_duration": (session.last_active - session.created_at).total_seconds(),
                        "messages": [asdict(msg) for msg in session.messages]
                    },
                    "analytics": {
                        "message_types": ChatDataManager._analyze_message_types(session.messages),
                        "character_participation": ChatDataManager._analyze_character_participation(session.messages),
                        "response_times": ChatDataManager._analyze_response_times(session.messages)
                    }
                }
                
                with open(export_file, 'w', encoding='utf-8') as f:
                    json.dump(export_data, f, ensure_ascii=False, indent=2)
                    
            elif export_format.lower() == "csv":
                import csv
                filename = f"export_{timestamp}_{safe_title}.csv"
                export_file = os.path.join(EXPORT_DIR, filename)
                
                with open(export_file, 'w', newline='', encoding='utf-8') as f:
                    writer = csv.writer(f)
                    writer.writerow(['Message ID', 'Character Name', 'Message', 'Timestamp', 'Type', 'Response Time (ms)'])
                    
                    for msg in session.messages:
                        writer.writerow([
                            msg.id,
                            msg.character_name,
                            msg.message,
                            msg.timestamp,
                            msg.message_type,
                            msg.response_time_ms or ''
                        ])
            
            print(f"📊 对话数据已导出: {export_file}")
            return export_file
            
        except Exception as e:
            print(f"❌ 导出对话数据失败: {e}")
            raise

    @staticmethod
    def _calculate_average_response_time(messages: List[ChatMessage]) -> float:
        """计算平均响应时间"""
        response_times = [msg.response_time_ms for msg in messages if msg.response_time_ms]
        return sum(response_times) / len(response_times) if response_times else 0

    @staticmethod
    def _analyze_message_types(messages: List[ChatMessage]) -> Dict[str, int]:
        """分析消息类型分布"""
        types = {}
        for msg in messages:
            types[msg.message_type] = types.get(msg.message_type, 0) + 1
        return types

    @staticmethod
    def _analyze_character_participation(messages: List[ChatMessage]) -> Dict[str, int]:
        """分析角色参与度"""
        participation = {}
        for msg in messages:
            participation[msg.character_name] = participation.get(msg.character_name, 0) + 1
        return participation

    @staticmethod
    def _analyze_response_times(messages: List[ChatMessage]) -> Dict[str, Any]:
        """分析响应时间统计"""
        response_times = [msg.response_time_ms for msg in messages if msg.response_time_ms]
        if not response_times:
            return {"average": 0, "min": 0, "max": 0, "count": 0}
        
        return {
            "average": sum(response_times) / len(response_times),
            "min": min(response_times),
            "max": max(response_times),
            "count": len(response_times)
        }

    @staticmethod
    def get_all_backups() -> List[Dict]:
        """获取所有备份信息"""
        try:
            backups = []
            for filename in os.listdir(BACKUP_DIR):
                if filename.endswith('.json') and filename.startswith('backup_'):
                    try:
                        backup_file = os.path.join(BACKUP_DIR, filename)
                        with open(backup_file, 'r', encoding='utf-8') as f:
                            data = json.load(f)
                        
                        backups.append({
                            "backup_id": data['backup_id'],
                            "filename": filename,
                            "theater_title": data['theater_title'],
                            "node_title": data['node_title'],
                            "backup_time": data['backup_time'],
                            "messages_count": data['messages_count'],
                            "file_size_bytes": os.path.getsize(backup_file)
                        })
                    except Exception as e:
                        print(f"⚠️ 读取备份文件失败 {filename}: {e}")
                        continue
            
            # 按备份时间排序
            backups.sort(key=lambda x: x['backup_time'], reverse=True)
            return backups
            
        except Exception as e:
            print(f"❌ 获取备份列表失败: {e}")
            return []

# ==================== 工具函数 ====================

async def find_theater_id_by_title(theater_title: str) -> Optional[str]:
    """根据剧场标题查找数据库中的theater_id"""
    try:
        global mysql_manager
        if not mysql_manager:
            return None
        
        loop = asyncio.get_event_loop()
        
        def search_theater():
            try:
                with mysql_manager.get_connection() as connection:
                    cursor = connection.cursor()
                    cursor.execute("SELECT id FROM theaters WHERE title = %s LIMIT 1", (theater_title,))
                    result = cursor.fetchone()
                    cursor.close()
                    return result
            except Exception as e:
                print(f"❌ 数据库查询失败: {e}")
                return None
        
        with ThreadPoolExecutor() as executor:
            result = await loop.run_in_executor(executor, search_theater)
            
            if result:
                print(f"✅ 找到剧场'{theater_title}'的数据库ID: {result[0]}")
                return str(result[0])
            
            print(f"⚠️ 数据库中未找到剧场'{theater_title}'")
            return None
            
    except Exception as e:
        print(f"❌ 查找剧场ID失败: {e}")
        return None

# ==================== 清理任务 ====================

async def cleanup_inactive_sessions():
    """清理不活跃的会话"""
    try:
        cutoff_time = datetime.now() - timedelta(hours=24)  # 24小时不活跃就清理
        
        inactive_sessions = [
            session_id for session_id, session in active_sessions.items()
            if session.last_active < cutoff_time
        ]
        
        for session_id in inactive_sessions:
            session = active_sessions[session_id]
            
            # 最后一次保存到数据库
            await ChatDataManager.save_session_async(session)
            print(f"🧹 清理不活跃会话: {session_id}")
            del active_sessions[session_id]
            
            # 清理AI上下文
            global chat_engine
            if chat_engine and session_id in chat_engine.conversation_contexts:
                del chat_engine.conversation_contexts[session_id]
        
        if inactive_sessions:
            print(f"✅ 已清理 {len(inactive_sessions)} 个不活跃会话")
            
    except Exception as e:
        print(f"❌ 清理会话失败: {e}")

async def cleanup_old_backups(days_old: int = 30):
    """清理旧备份文件"""
    try:
        cutoff_time = datetime.now() - timedelta(days=days_old)
        deleted_count = 0
        
        for filename in os.listdir(BACKUP_DIR):
            if filename.endswith('.json') and filename.startswith('backup_'):
                file_path = os.path.join(BACKUP_DIR, filename)
                file_mtime = datetime.fromtimestamp(os.path.getmtime(file_path))
                
                if file_mtime < cutoff_time:
                    os.remove(file_path)
                    deleted_count += 1
                    print(f"🗑️ 删除旧备份: {filename}")
        
        if deleted_count > 0:
            print(f"✅ 已清理 {deleted_count} 个旧备份文件")
            
    except Exception as e:
        print(f"❌ 清理备份失败: {e}")

async def start_periodic_cleanup():
    """启动定期清理任务"""
    while True:
        await asyncio.sleep(3600)  # 每小时执行一次
        await cleanup_inactive_sessions()
        await cleanup_old_backups()

# ==================== Lifespan事件处理器 ====================

@asynccontextmanager
async def lifespan(app: FastAPI):
    """FastAPI应用生命周期管理"""
    # 启动时执行
    print("🚀 启动剧场聊天API服务...")
    
    # 初始化MySQL管理器
    global mysql_manager
    if DATABASE_AVAILABLE:
        try:
            mysql_manager = get_mysql_manager()
            print("✅ MySQL数据库管理器初始化成功")
        except Exception as e:
            print(f"❌ MySQL数据库管理器初始化失败: {e}")
            mysql_manager = None
    else:
        print("⚠️ 数据库模块不可用，使用文件存储模式")
    
    # 初始化AI引擎
    global chat_engine
    try:
        chat_engine = TheaterChatEngine()
        print("✅ 聊天引擎初始化成功")
    except Exception as e:
        print(f"❌ 聊天引擎初始化失败: {e}")
        print("请确保设置了GEEKAI_API_KEY环境变量")
        chat_engine = None
    
    # 启动定期清理任务
    cleanup_task = asyncio.create_task(start_periodic_cleanup())
    print("✅ 定期清理任务已启动")
    
    yield  # 应用运行期间
    
    # 关闭时执行
    print("🛑 正在关闭剧场聊天API服务...")
    
    # 取消清理任务
    cleanup_task.cancel()
    try:
        await cleanup_task
    except asyncio.CancelledError:
        print("✅ 定期清理任务已停止")
    
    # 保存所有活跃会话 - 改为异步调用
    for session_id, session in active_sessions.items():
        try:
            await ChatDataManager.save_session_async(session)
            print(f"💾 已保存会话到数据库: {session_id}")
        except Exception as e:
            print(f"❌ 保存会话 {session_id} 失败: {e}")
    
    # 关闭MySQL连接
    if mysql_manager:
        mysql_manager.close()
        print("✅ MySQL连接已关闭")
    
    print("✅ 剧场聊天API服务已关闭")

# ==================== FastAPI应用 ====================

app = FastAPI(
    title="剧场聊天API",
    description="剧场角色对话和AI回复系统 - 集成MySQL数据库 - Vue前端完全集成版",
    version="1.3.0",
    docs_url="/docs",
    redoc_url="/redoc",
    lifespan=lifespan  # 使用新的lifespan事件处理器
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# ==================== API端点 ====================

@app.get("/", response_model=ApiResponse)
async def root():
    return ApiResponse(
        success=True,
        message="剧场聊天API服务正在运行",
        data={
            "version": "1.3.0",
            "docs": "/docs",
            "active_sessions": len(active_sessions),
            "engine_ready": chat_engine is not None,
            "mysql_ready": mysql_manager is not None,
            "database_mode": "MySQL" if DATABASE_AVAILABLE and mysql_manager else "File Only",
            "storage_directories": {
                "sessions": STORAGE_DIR,
                "backups": BACKUP_DIR,
                "exports": EXPORT_DIR
            }
        }
    )

@app.get("/health", response_model=ApiResponse)
async def health_check():
    try:
        if not chat_engine:
            return ApiResponse(
                success=False,
                message="聊天引擎未初始化",
                error_code="ENGINE_NOT_READY"
            )
        
        # 统计存储信息
        total_sessions = 0
        if DATABASE_AVAILABLE and mysql_manager:
            # 从数据库统计
            try:
                with mysql_manager.get_connection() as connection:
                    cursor = connection.cursor()
                    cursor.execute("SELECT COUNT(*) FROM chat_sessions")
                    total_sessions = cursor.fetchone()[0]
                    cursor.close()
            except:
                pass
        
        if total_sessions == 0:
            # 从文件统计
            total_sessions = sum(len(os.listdir(os.path.join(STORAGE_DIR, d))) 
                               for d in os.listdir(STORAGE_DIR) 
                               if os.path.isdir(os.path.join(STORAGE_DIR, d)))
        
        total_backups = len([f for f in os.listdir(BACKUP_DIR) if f.endswith('.json')])
        
        return ApiResponse(
            success=True,
            message="服务正常运行",
            data={
                "status": "healthy",
                "active_sessions": len(active_sessions),
                "total_saved_sessions": total_sessions,
                "total_backups": total_backups,
                "mysql_ready": mysql_manager is not None,
                "database_available": DATABASE_AVAILABLE,
                "ai_engine_ready": chat_engine is not None,
                "timestamp": datetime.now().isoformat()
            }
        )
    except Exception as e:
        return ApiResponse(
            success=False,
            message=f"健康检查失败: {str(e)}",
            error_code="HEALTH_CHECK_FAILED"
        )

@app.post("/chat/start", response_model=ApiResponse)
async def start_chat_session(request: StartChatRequest):
    """开始聊天会话 - 确保正确的数据库关联"""
    try:
        if not chat_engine:
            raise HTTPException(status_code=500, detail="聊天引擎未初始化")
        
        # 生成会话ID
        session_id = str(uuid.uuid4())
        
        # 🎯 改进：尝试从数据库查找正确的theater_id
        theater_id = None
        if DATABASE_AVAILABLE and mysql_manager:
            theater_id = await find_theater_id_by_title(request.theater_title)
        
        if not theater_id:
            # 如果数据库中没找到，使用哈希生成（向后兼容）
            theater_id = str(hash(request.theater_title) & 0x7fffffff)
            print(f"⚠️ 数据库中未找到剧场'{request.theater_title}'，使用哈希ID: {theater_id}")
        else:
            print(f"✅ 找到剧场'{request.theater_title}'的数据库ID: {theater_id}")
        
        # 转换角色数据
        characters = [Character(**char_data) for char_data in request.all_characters]
        
        # 尝试加载现有会话
        existing_session = ChatDataManager.load_session(theater_id, request.node_id)
        
        if existing_session:
            # 更新现有会话
            existing_session.session_id = session_id
            existing_session.last_active = datetime.now()
            
            # 使用现有会话数据
            session = existing_session
            
            print(f"📁 加载现有会话: {theater_id}/{request.node_id}, 消息数: {len(session.messages)}")
        else:
            # 创建新会话
            initial_messages = [
                ChatMessage(
                    id=str(uuid.uuid4()),
                    character_id="system",
                    character_name="系统",
                    message=f"欢迎来到《{request.theater_title}》的「{request.node_title}」场景！",
                    timestamp=datetime.now().isoformat(),
                    message_type="system"
                ),
                ChatMessage(
                    id=str(uuid.uuid4()),
                    character_id="system", 
                    character_name="系统",
                    message="您可以开始与其他角色对话了。请输入您的对话内容...",
                    timestamp=datetime.now().isoformat(),
                    message_type="system"
                )
            ]
            
            session = ChatSession(
                session_id=session_id,
                theater_id=theater_id,
                theater_title=request.theater_title,
                node_id=request.node_id,
                node_title=request.node_title,
                node_content=request.node_content,
                player_character_id=request.player_character_id,
                all_characters=characters,
                character_settings=request.character_settings,
                messages=initial_messages,
                created_at=datetime.now(),
                last_active=datetime.now(),
                total_messages=2,
                ai_responses_count=0
            )
            
            print(f"🆕 创建新会话: {theater_id}/{request.node_id}")
        
        # 保存到活跃会话
        active_sessions[session_id] = session
        
        # 初始化AI上下文
        chat_engine.initialize_session_context(session)
        
        # 🎯 保存会话数据（文件+数据库）
        await ChatDataManager.save_session_async(session)
        
        return ApiResponse(
            success=True,
            message="聊天会话已启动",
            data={
                "session_id": session_id,
                "theater_id": theater_id,
                "initial_messages": [asdict(msg) for msg in session.messages],
                "session_stats": {
                    "total_messages": session.total_messages,
                    "ai_responses_count": session.ai_responses_count
                },
                "database_linked": theater_id is not None,
                "characters_count": len(session.all_characters)
            }
        )
        
    except Exception as e:
        print(f"❌ 启动聊天会话失败: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"启动会话失败: {str(e)}"
        )

@app.post("/chat/message", response_model=ApiResponse)
async def send_message(request: SendMessageRequest):
    """发送消息并获取AI回复 - 增强数据库同步版"""
    try:
        session_id = request.session_id
        
        if session_id not in active_sessions:
            raise HTTPException(
                status_code=404,
                detail="会话不存在或已过期"
            )
        
        session = active_sessions[session_id]
        
        # 特殊处理继续对话请求
        if request.message == "[CONTINUE_DIALOGUE]":
            print("🔄 收到继续对话请求，生成AI角色回复...")
            
            # 生成AI角色回复
            ai_responses = await chat_engine.generate_character_responses(session, request.message)
            
            # 添加AI回复到会话（不添加特殊消息本身）
            session.messages.extend(ai_responses)
            
            # 更新统计和时间
            session.last_active = datetime.now()
            session.total_messages = len(session.messages)
            session.ai_responses_count += len([r for r in ai_responses if r.message_type == "dialogue"])
            
            # 🎯 重点：每次对话后立即异步同步到数据库
            try:
                async def sync_to_database():
                    try:
                        await ChatDataManager.save_session_async(session)
                        print(f"🔄 会话 {session_id} 数据已同步到数据库")
                    except Exception as e:
                        print(f"❌ 后台数据库同步失败: {e}")
                
                # 创建后台任务
                asyncio.create_task(sync_to_database())
                
                # 同时保存到文件作为备份
                ChatDataManager._save_session_to_file(session)
                
            except Exception as e:
                print(f"⚠️ 数据同步过程出现问题: {e}")
            
            return ApiResponse(
                success=True,
                message="AI角色回复生成成功",
                data={
                    "new_messages": [asdict(msg) for msg in ai_responses],
                    "session_stats": {
                        "total_messages": session.total_messages,
                        "ai_responses_count": session.ai_responses_count,
                        "last_sync": datetime.now().isoformat()
                    }
                }
            )
        
        # 正常的玩家消息处理
        # 添加玩家消息
        player_char = next((c for c in session.all_characters if c.id == session.player_character_id), None)
        player_name = player_char.name if player_char else "玩家"
        
        player_message = ChatMessage(
            id=str(uuid.uuid4()),
            character_id=session.player_character_id,
            character_name=player_name,
            message=request.message,
            timestamp=datetime.now().isoformat(),
            message_type="dialogue"
        )
        
        session.messages.append(player_message)
        
        # 生成AI角色回复
        ai_responses = await chat_engine.generate_character_responses(session, request.message)
        
        # 添加AI回复到会话
        session.messages.extend(ai_responses)
        
        # 更新统计和时间
        session.last_active = datetime.now()
        session.total_messages = len(session.messages)
        session.ai_responses_count += len([r for r in ai_responses if r.message_type == "dialogue"])
        
        # 🎯 重点：每次对话后立即异步同步到数据库
        try:
            # 使用后台任务确保数据库同步不会阻塞响应
            async def sync_to_database():
                try:
                    await ChatDataManager.save_session_async(session)
                    print(f"🔄 会话 {session_id} 数据已同步到数据库")
                except Exception as e:
                    print(f"❌ 后台数据库同步失败: {e}")
            
            # 创建后台任务
            asyncio.create_task(sync_to_database())
            
            # 同时保存到文件作为备份
            ChatDataManager._save_session_to_file(session)
            
        except Exception as e:
            print(f"⚠️ 数据同步过程出现问题: {e}")
        
        # 准备返回的新消息（包括玩家消息和AI回复）
        new_messages = [player_message] + ai_responses
        
        print(f"💬 会话 {session_id}: 玩家发言 + {len(ai_responses)} 个AI回复 (总消息: {session.total_messages})")
        print(f"📊 数据同步: 文件✅ 数据库🔄")
        
        return ApiResponse(
            success=True,
            message="消息发送成功",
            data={
                "new_messages": [asdict(msg) for msg in new_messages],
                "session_stats": {
                    "total_messages": session.total_messages,
                    "ai_responses_count": session.ai_responses_count,
                    "last_sync": datetime.now().isoformat()
                }
            }
        )
        
    except HTTPException:
        raise
    except Exception as e:
        print(f"❌ 发送消息失败: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"发送消息失败: {str(e)}"
        )

@app.get("/chat/history/{session_id}", response_model=ApiResponse)
async def get_chat_history(session_id: str):
    """获取聊天历史"""
    try:
        if session_id not in active_sessions:
            raise HTTPException(
                status_code=404,
                detail="会话不存在"
            )
        
        session = active_sessions[session_id]
        
        # 分析消息统计
        message_stats = {
            "total": len(session.messages),
            "by_type": ChatDataManager._analyze_message_types(session.messages),
            "by_character": ChatDataManager._analyze_character_participation(session.messages),
            "response_times": ChatDataManager._analyze_response_times(session.messages)
        }
        
        return ApiResponse(
            success=True,
            message="获取聊天历史成功",
            data={
                "session_id": session_id,
                "messages": [asdict(msg) for msg in session.messages],
                "theater_title": session.theater_title,
                "node_title": session.node_title,
                "session_info": {
                    "created_at": session.created_at.isoformat(),
                    "last_active": session.last_active.isoformat(),
                    "total_messages": session.total_messages,
                    "ai_responses_count": session.ai_responses_count
                },
                "statistics": message_stats
            }
        )
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"获取聊天历史失败: {str(e)}"
        )

@app.get("/chat/sessions/{theater_id}", response_model=ApiResponse)
async def get_theater_sessions(theater_id: str):
    """获取剧场的所有会话"""
    try:
        sessions = ChatDataManager.get_theater_sessions(theater_id)
        
        return ApiResponse(
            success=True,
            message="获取剧场会话成功",
            data={
                "theater_id": theater_id,
                "sessions": sessions,
                "total": len(sessions),
                "statistics": {
                    "total_messages": sum(s.get('message_count', s.get('total_messages', 0)) for s in sessions),
                    "total_ai_responses": sum(s.get('ai_responses_count', 0) for s in sessions),
                    "total_size_bytes": sum(s.get('file_size_bytes', 0) for s in sessions)
                }
            }
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"获取剧场会话失败: {str(e)}"
        )

@app.delete("/chat/session/{session_id}", response_model=ApiResponse)
async def delete_chat_session(session_id: str):
    """删除聊天会话"""
    try:
        if session_id in active_sessions:
            session = active_sessions[session_id]
            
            # 创建最终备份
            try:
                ChatDataManager.create_conversation_backup(session)
            except Exception as e:
                print(f"⚠️ 创建删除前备份失败: {e}")
            
            del active_sessions[session_id]
        
        # 清理AI上下文
        if chat_engine and session_id in chat_engine.conversation_contexts:
            del chat_engine.conversation_contexts[session_id]
        
        return ApiResponse(
            success=True,
            message="会话已删除"
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"删除会话失败: {str(e)}"
        )

# ==================== 数据同步监控端点 ====================

@app.get("/chat/sync/status/{session_id}", response_model=ApiResponse)
async def get_sync_status(session_id: str):
    """获取会话的数据同步状态"""
    try:
        if session_id not in active_sessions:
            raise HTTPException(status_code=404, detail="会话不存在")
        
        session = active_sessions[session_id]
        
        # 检查数据库中的数据
        db_session = None
        if DATABASE_AVAILABLE and mysql_manager:
            try:
                db_session = mysql_manager.get_chat_session_by_theater_node(
                    int(session.theater_id), session.node_id
                )
            except Exception as e:
                print(f"❌ 检查数据库状态失败: {e}")
        
        # 检查文件中的数据
        file_exists = os.path.exists(os.path.join(
            STORAGE_DIR, 
            f"theater_{session.theater_id}", 
            f"node_{session.node_id}_session.json"
        ))
        
        sync_status = {
            "session_id": session_id,
            "theater_id": session.theater_id,
            "node_id": session.node_id,
            "local_messages": len(session.messages),
            "database_sync": {
                "connected": mysql_manager is not None,
                "available": DATABASE_AVAILABLE,
                "session_exists": db_session is not None,
                "db_messages": len(db_session.get('messages', [])) if db_session else 0,
                "last_db_update": db_session.get('last_active') if db_session else None
            },
            "file_sync": {
                "file_exists": file_exists,
                "last_file_update": session.last_active.isoformat()
            },
            "sync_health": {
                "database_synced": db_session is not None and len(db_session.get('messages', [])) == len(session.messages),
                "file_synced": file_exists,
                "overall_health": "good" if (db_session and file_exists) else "partial"
            }
        }
        
        return ApiResponse(
            success=True,
            message="同步状态检查完成",
            data=sync_status
        )
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"检查同步状态失败: {str(e)}"
        )

@app.post("/chat/sync/force/{session_id}", response_model=ApiResponse)
async def force_sync_session(session_id: str):
    """强制同步会话数据到数据库"""
    try:
        if session_id not in active_sessions:
            raise HTTPException(status_code=404, detail="会话不存在")
        
        session = active_sessions[session_id]
        
        # 强制同步到数据库
        await ChatDataManager.save_session_async(session)
        
        return ApiResponse(
            success=True,
            message="数据强制同步完成",
            data={
                "session_id": session_id,
                "theater_id": session.theater_id,
                "messages_synced": len(session.messages),
                "sync_timestamp": datetime.now().isoformat(),
                "database_available": DATABASE_AVAILABLE
            }
        )
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"强制同步失败: {str(e)}"
        )

@app.post("/chat/sync/batch", response_model=ApiResponse)
async def batch_sync_all_sessions():
    """批量同步所有活跃会话到数据库"""
    try:
        if not DATABASE_AVAILABLE or not mysql_manager:
            raise HTTPException(status_code=503, detail="数据库未初始化")
        
        sync_results = []
        
        for session_id, session in active_sessions.items():
            try:
                await ChatDataManager.save_session_async(session)
                sync_results.append({
                    "session_id": session_id,
                    "theater_id": session.theater_id,
                    "status": "success",
                    "messages_count": len(session.messages)
                })
            except Exception as e:
                sync_results.append({
                    "session_id": session_id,
                    "theater_id": session.theater_id,
                    "status": "failed",
                    "error": str(e)
                })
        
        success_count = len([r for r in sync_results if r["status"] == "success"])
        
        return ApiResponse(
            success=True,
            message=f"批量同步完成: {success_count}/{len(sync_results)} 成功",
            data={
                "total_sessions": len(sync_results),
                "success_count": success_count,
                "failed_count": len(sync_results) - success_count,
                "details": sync_results
            }
        )
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"批量同步失败: {str(e)}"
        )

# ==================== 数据库管理端点 ====================

@app.get("/chat/database/sessions/{theater_id}", response_model=ApiResponse)
async def get_theater_chat_sessions_from_db(theater_id: int, limit: int = 50, offset: int = 0):
    """从数据库获取剧场的聊天会话"""
    try:
        if not DATABASE_AVAILABLE or not mysql_manager:
            raise HTTPException(status_code=503, detail="数据库未初始化")
        
        result = mysql_manager.get_chat_sessions_by_theater(theater_id, limit, offset)
        
        return ApiResponse(
            success=result.get('success', False),
            message="获取数据库聊天会话成功" if result.get('success') else "获取失败",
            data=result
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"获取数据库聊天会话失败: {str(e)}"
        )

@app.get("/chat/database/messages/{session_id}", response_model=ApiResponse)
async def get_session_messages_from_db(session_id: str):
    """从数据库获取会话消息"""
    try:
        if not DATABASE_AVAILABLE or not mysql_manager:
            raise HTTPException(status_code=503, detail="数据库未初始化")
        
        result = mysql_manager.get_chat_messages_by_session(session_id)
        
        return ApiResponse(
            success=result.get('success', False),
            message="获取数据库聊天消息成功" if result.get('success') else "获取失败",
            data=result
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"获取数据库聊天消息失败: {str(e)}"
        )

@app.get("/chat/database/statistics/{theater_id}", response_model=ApiResponse)
async def get_theater_chat_statistics_from_db(theater_id: int):
    """从数据库获取剧场聊天统计"""
    try:
        if not DATABASE_AVAILABLE or not mysql_manager:
            raise HTTPException(status_code=503, detail="数据库未初始化")
        
        result = mysql_manager.get_chat_statistics_by_theater(theater_id)
        
        return ApiResponse(
            success=result.get('success', False),
            message="获取聊天统计成功" if result.get('success') else "获取失败",
            data=result
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"获取聊天统计失败: {str(e)}"
        )

@app.post("/chat/database/migrate/{theater_id}", response_model=ApiResponse)
async def migrate_theater_chat_data(theater_id: str):
    """将文件中的聊天数据迁移到数据库"""
    try:
        if not DATABASE_AVAILABLE or not mysql_manager:
            raise HTTPException(status_code=503, detail="数据库未初始化")
        
        migrated_count = 0
        
        # 获取剧场目录下的所有会话文件
        theater_dir = os.path.join(STORAGE_DIR, f"theater_{theater_id}")
        if not os.path.exists(theater_dir):
            return ApiResponse(
                success=False,
                message="剧场目录不存在",
                error_code="THEATER_DIR_NOT_FOUND"
            )
        
        for filename in os.listdir(theater_dir):
            if filename.endswith('_session.json'):
                try:
                    # 从文件加载会话
                    node_id = filename.replace('node_', '').replace('_session.json', '')
                    session = ChatDataManager._load_session_from_file(theater_id, node_id)
                    
                    if session:
                        # 保存到数据库
                        await ChatDataManager._save_session_to_mysql_enhanced(session)
                        migrated_count += 1
                        print(f"✅ 迁移会话: {session.session_id}")
                        
                except Exception as e:
                    print(f"⚠️ 迁移文件 {filename} 失败: {e}")
                    continue
        
        return ApiResponse(
            success=True,
            message=f"迁移完成，共处理 {migrated_count} 个会话",
            data={
                "theater_id": theater_id,
                "migrated_sessions": migrated_count
            }
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"数据迁移失败: {str(e)}"
        )

@app.get("/chat/database/test", response_model=ApiResponse)
async def test_chat_database_connection():
    """测试聊天数据库连接"""
    try:
        if not DATABASE_AVAILABLE:
            return ApiResponse(
                success=False,
                message="数据库模块不可用",
                error_code="DATABASE_MODULE_NOT_AVAILABLE"
            )
        
        if not mysql_manager:
            return ApiResponse(
                success=False,
                message="数据库管理器未初始化",
                error_code="DATABASE_NOT_READY"
            )
        
        # 测试连接
        with mysql_manager.get_connection() as connection:
            cursor = connection.cursor()
            
            # 检查聊天相关表是否存在
            cursor.execute("SHOW TABLES LIKE 'chat_%'")
            chat_tables = [row[0] for row in cursor.fetchall()]
            
            # 获取聊天数据统计
            cursor.execute("SELECT COUNT(*) FROM chat_sessions")
            total_sessions = cursor.fetchone()[0]
            
            cursor.execute("SELECT COUNT(*) FROM chat_messages")
            total_messages = cursor.fetchone()[0]
            
            cursor.close()
        
        return ApiResponse(
            success=True,
            message="聊天数据库连接正常",
            data={
                "chat_tables": chat_tables,
                "total_sessions": total_sessions,
                "total_messages": total_messages,
                "database_available": True,
                "timestamp": datetime.now().isoformat()
            }
        )
        
    except Exception as e:
        return ApiResponse(
            success=False,
            message=f"聊天数据库连接失败: {str(e)}",
            error_code="DATABASE_CONNECTION_FAILED"
        )

# ==================== 数据管理端点 ====================

@app.post("/chat/export/{session_id}", response_model=ApiResponse)
async def export_conversation(session_id: str, export_format: str = "json"):
    """导出对话数据"""
    try:
        if session_id not in active_sessions:
            raise HTTPException(status_code=404, detail="会话不存在")
        
        session = active_sessions[session_id]
        export_file = ChatDataManager.export_conversation_for_analysis(session, export_format)
        
        return ApiResponse(
            success=True,
            message=f"对话数据已导出为 {export_format.upper()} 格式",
            data={
                "export_file": export_file,
                "format": export_format,
                "messages_count": len(session.messages)
            }
        )
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"导出失败: {str(e)}"
        )

@app.get("/chat/export/{session_id}/download")
async def download_conversation_export(session_id: str, export_format: str = "json"):
    """下载导出的对话文件"""
    try:
        if session_id not in active_sessions:
            raise HTTPException(status_code=404, detail="会话不存在")
        
        session = active_sessions[session_id]
        export_file = ChatDataManager.export_conversation_for_analysis(session, export_format)
        
        if not os.path.exists(export_file):
            raise HTTPException(status_code=404, detail="导出文件不存在")
        
        return FileResponse(
            path=export_file,
            filename=os.path.basename(export_file),
            media_type='application/octet-stream'
        )
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"下载失败: {str(e)}"
        )

@app.get("/chat/backups", response_model=ApiResponse)
async def list_conversation_backups():
    """获取所有对话备份"""
    try:
        backups = ChatDataManager.get_all_backups()
        
        return ApiResponse(
            success=True,
            message="获取备份列表成功",
            data={
                "backups": backups,
                "total": len(backups),
                "total_size_bytes": sum(b['file_size_bytes'] for b in backups)
            }
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"获取备份列表失败: {str(e)}"
        )

@app.post("/chat/backup/{session_id}", response_model=ApiResponse)
async def create_manual_backup(session_id: str):
    """手动创建对话备份"""
    try:
        if session_id not in active_sessions:
            raise HTTPException(status_code=404, detail="会话不存在")
        
        session = active_sessions[session_id]
        backup = ChatDataManager.create_conversation_backup(session)
        
        return ApiResponse(
            success=True,
            message="手动备份创建成功",
            data={
                "backup_id": backup.backup_id,
                "file_path": backup.file_path,
                "messages_count": backup.messages_count
            }
        )
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"创建备份失败: {str(e)}"
        )

@app.get("/chat/stats", response_model=ApiResponse)
async def get_chat_statistics():
    """获取聊天系统统计信息"""
    try:
        # 统计活跃会话
        active_stats = {
            "total_active_sessions": len(active_sessions),
            "total_active_messages": sum(len(s.messages) for s in active_sessions.values()),
            "average_messages_per_session": 0
        }
        
        if active_sessions:
            active_stats["average_messages_per_session"] = active_stats["total_active_messages"] / len(active_sessions)
        
        # 统计存储数据
        total_theaters = 0
        total_sessions = 0
        total_messages = 0
        
        if DATABASE_AVAILABLE and mysql_manager:
            # 从数据库统计
            try:
                with mysql_manager.get_connection() as connection:
                    cursor = connection.cursor()
                    
                    cursor.execute("SELECT COUNT(DISTINCT theater_id) FROM chat_sessions")
                    total_theaters = cursor.fetchone()[0]
                    
                    cursor.execute("SELECT COUNT(*) FROM chat_sessions")
                    total_sessions = cursor.fetchone()[0]
                    
                    cursor.execute("SELECT COUNT(*) FROM chat_messages")
                    total_messages = cursor.fetchone()[0]
                    
                    cursor.close()
            except:
                pass
        
        if total_sessions == 0:
            # 从文件统计
            total_theaters = len([d for d in os.listdir(STORAGE_DIR) if os.path.isdir(os.path.join(STORAGE_DIR, d))])
            total_sessions = sum(len(os.listdir(os.path.join(STORAGE_DIR, d))) 
                               for d in os.listdir(STORAGE_DIR) 
                               if os.path.isdir(os.path.join(STORAGE_DIR, d)))
        
        total_backups = len([f for f in os.listdir(BACKUP_DIR) if f.endswith('.json')])
        total_exports = len([f for f in os.listdir(EXPORT_DIR) if f.endswith(('.json', '.csv'))])
        
        return ApiResponse(
            success=True,
            message="获取统计信息成功",
            data={
                "active_sessions": active_stats,
                "storage_stats": {
                    "total_theaters": total_theaters,
                    "total_saved_sessions": total_sessions,
                    "total_messages": total_messages,
                    "total_backups": total_backups,
                    "total_exports": total_exports
                },
                "system_info": {
                    "engine_ready": chat_engine is not None,
                    "mysql_ready": mysql_manager is not None,
                    "database_available": DATABASE_AVAILABLE,
                    "active_ai_contexts": len(chat_engine.conversation_contexts) if chat_engine else 0,
                    "timestamp": datetime.now().isoformat(),
                    "version": "1.3.0"
                }
            }
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"获取统计信息失败: {str(e)}"
        )

# ==================== 异常处理 ====================

@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc):
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "success": False,
            "message": str(exc.detail),
            "error_code": "HTTP_ERROR"
        }
    )

@app.exception_handler(Exception)
async def general_exception_handler(request, exc):
    print(f"❌ 服务器错误: {exc}")
    return JSONResponse(
        status_code=500,
        content={
            "success": False,
            "message": f"服务器内部错误: {str(exc)}",
            "error_code": "INTERNAL_SERVER_ERROR"
        }
    )

# ==================== 启动配置 ====================

def main():
    """主函数"""
    if not os.getenv('GEEKAI_API_KEY'):
        print("⚠️  请设置GEEKAI_API_KEY环境变量")
        print("   例如: export GEEKAI_API_KEY='your-api-key'")
        print("   或者: set GEEKAI_API_KEY=your-api-key (Windows)")
        return False
    
    print("💬 启动剧场聊天API服务...")
    print(f"📖 API文档: http://localhost:8801/docs")
    print(f"🔍 Redoc文档: http://localhost:8801/redoc")
    print("💡 按 Ctrl+C 停止服务")
    print()
    print("🚀 支持的功能:")
    print("   - 🎭 角色对话系统")
    print("   - 🤖 AI智能回复")
    print("   - 💾 会话持久化")
    print("   - 🗄️ MySQL数据库存储")
    print("   - 🔗 数据关联存储")
    print("   - 🔄 实时数据同步")
    print("   - 🧹 自动清理机制")
    print("   - 📦 自动备份功能")
    print("   - 📊 数据导出分析")
    print("   - 📈 统计信息监控")
    print("   - ⏱️ 响应时间追踪")
    print("   - 🔄 数据迁移工具")
    print("   - 📋 同步状态监控")
    print("   - 🎮 Vue前端完全集成")
    print("   - 🔄 继续对话功能")
    print()
    print("📁 数据存储:")
    if DATABASE_AVAILABLE:
        print(f"   - MySQL数据库: theam")
    else:
        print(f"   - 数据库: 不可用（使用文件存储）")
    print(f"   - 会话备份: {STORAGE_DIR}/")
    print(f"   - 对话备份: {BACKUP_DIR}/")
    print(f"   - 数据导出: {EXPORT_DIR}/")
    
    return True

if __name__ == "__main__":
    if main():
        uvicorn.run(
            "chat_api:app",
            host="0.0.0.0",
            port=8801,
            reload=True,
            log_level="info",
            access_log=True
        )
